package com.rcache.core.manager;


import com.rcache.core.cache.CacheEnum;
import com.rcache.core.cache.Rcache;
import com.rcache.core.cache.strategy.FIFOCache;
import com.rcache.core.config.CacheConfiguration;
import com.rcache.core.config.ICacheConfiguration;
import com.rcache.core.distributed.IDistributedAgency;
import com.rcache.core.exception.ConfigurationException;
import com.rcache.core.exception.DataPersistenceException;
import com.rcache.core.netty.DistributedAgency;
import com.rcache.core.persistence.*;
import com.rcache.core.proxy.JDKProxy;
import org.apache.log4j.Logger;

import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @desc 缓存管理器
 * @author gzy
 * @time 2022-05-26
 */
public class CacheManager implements ICacheManager{

    /**
     * @desc 存储缓存的map
     */
    private ConcurrentHashMap<Object, Rcache> caches=new ConcurrentHashMap<Object, Rcache>();
    /**
     * @desc 单例
     */
    private static CacheManager cacheManager;
    private CacheManager(){}
    private static CacheConfiguration configuration;
    static Logger LOG=Logger.getLogger(CacheManager.class);
    /**
     * @desc 获得实例，寻找class下配置文件，如果不存在则加载默认配置
     * @return cacheManager
     */
    public static CacheManager newInstance() throws ConfigurationException {
        if (cacheManager!=null){
            return cacheManager;
        }
        synchronized(CacheManager.class){
            //创建实例
            cacheManager=new CacheManager();

            configuration=new CacheConfiguration();

            //加载默认配置
            Map<String, Rcache> cacheMap=configuration.init("default");
            for (String key:cacheMap.keySet()) {
                Rcache cache = cacheMap.get(key);

                JDKProxy jdkProxy = new JDKProxy();
                Rcache cacheProxy = (Rcache) jdkProxy.newProxy(cache);
                cacheManager.caches.put(key,cacheProxy);
            }

        }
        return cacheManager;
    }



    /**
     * @desc 获得实例，寻找class下配置文件，如果不存在则加载默认配置
     * @return cacheManager
     */
    public static CacheManager newInstance(String configFilePath) throws ConfigurationException, DataPersistenceException {
        if (cacheManager!=null){
            return cacheManager;
        }
        synchronized(CacheManager.class){
            //创建实例
            cacheManager=new CacheManager();
            ICacheConfiguration cacheConfiguration=new CacheConfiguration();
            //加载默认配置
            Map<String, Rcache> cacheMap=cacheConfiguration.init(configFilePath);
            //加载默认配置
            for (String key:cacheMap.keySet()) {
                Rcache cache = cacheMap.get(key);

                JDKProxy jdkProxy = new JDKProxy();
                Rcache cacheProxy = (Rcache) jdkProxy.newProxy(cache);
                System.out.println(cacheMap.keySet()+":"+cacheProxy);
                cacheManager.caches.put(key,cacheProxy);
                //如果开启了分布式则开启分布式服务
                if (cacheProxy.isDistribute()){
                    startDistribute(cacheProxy);
                }
                //如果开启了持久化缓存则尝试从本地恢复
                if (cacheProxy.isDiskCache()){
                    tryRestoreFromLocal(cacheProxy);
                }

            }
        }
        return cacheManager;
    }

    /**
     * @desc 尝试从本地恢复
     * @param cacheProxy
     */
    private static void tryRestoreFromLocal(Rcache cacheProxy) throws DataPersistenceException {
        try {
            String diskCacheStrategy = cacheProxy.getDiskCacheStrategy();
            IDataPersistence dataPersistence=null;
            if (diskCacheStrategy.equals(EPersistence.TIME)){
                dataPersistence = DataTimePersistenceImpl.getInstance(cacheProxy);
            }else if (diskCacheStrategy.equals(EPersistence.SIZE)){
                dataPersistence = DateSizePersistenceImpl.getInstance(cacheProxy);
            }else {
                dataPersistence = DataPersistenceImpl.getInstance();
            }

            dataPersistence.tryRestoreFromLocal(cacheProxy);
            LOG.debug("从本地恢复成功");
        } catch (DataPersistenceException e) {
            throw new DataPersistenceException("尝试从本地恢复异常");
        }
    }

    /**
     * @desc 开启分布式
     */
    private static void startDistribute(Rcache cacheProxy) {
            IDistributedAgency distributedAgency;
            //防止重复开启
            if (cacheProxy.getAgency()==null){
                //获取本机的分布式代理
                distributedAgency= DistributedAgency.getInstance(cacheProxy);
                cacheProxy.setAgency(distributedAgency);
            }
    }

    @Override
    public Rcache getCache(String name) {
        return caches.get(name);
    }

    @Override
    public Collection<Object> getCacheNames() {
        return caches.keySet();
    }

    @Override
    public void addCache(Rcache rcache) {
        caches.put(rcache.getCacheName(),rcache);
    }

    @Override
    public void addCache(Object key, Object value)  {
        String cacheStrategy = configuration.getCacheStrategy();
        if(cacheStrategy.equals(CacheEnum.FIFO.getCacheName())){
            System.out.println("ljc:"+value);
            Rcache rcache=new FIFOCache<String,Object>();
            rcache.put(key,value);
            addCache(rcache);
        }
    }
    public  boolean baddCache(Object key, Object value){
        try {
            addCache(key,value);
            return true;
        } catch (Exception e) {

            return false;
        }
    }
    public boolean bupdateCache(Object key, Object value){
        try {
            updateCache(key,value);
            return true;
        } catch (Exception e) {

            return false;
        }
    }
    @Override
    public void updateCache(Object key, Object value){
        String cacheStrategy = configuration.getCacheStrategy();
        if(cacheStrategy.equals(CacheEnum.FIFO.getCacheName())){
            Rcache rcache=new FIFOCache<String,Object>();
            rcache.put(key,value);
            updateCache(rcache);
        }
    }

    @Override
    public void updateCache(Rcache rcache) {
       caches.put(rcache.getCacheName(),rcache);
    }

    @Override
    public void clear() {
        caches.clear();
    }

    @Override
    public void remove(Object key) {
        caches.remove(key);
    }

    @Override
    public void destroy() {
        cacheManager=null;
    }

    public long getSizeInMemory(){
        return configuration.getMaxElements();
     }

     public long getElementCountInMemory(){
        return caches.size();
     }

     public Map toMap(){
        return  caches;
     }

     public boolean contains(Object key){
         Rcache rcache = getCache(key + "");
         if (rcache!=null)return true;else return false;
     }

    public int getTimeout() {
        return configuration.getMaxTimeToLive();
    }

}
